from datetime import datetime
from typing import List, Tuple, Optional, Dict
from fastapi import HTTPException, UploadFile
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise.expressions import Q
from model import SysUser
from model.sysUserPostModel import SysUserPost
from model.sysUserRoleModel import SysUserRole
from schema.sysUserSchema import UserCreate, UserUpdate, PageParams, UserInfo, UserChange, UserProfile, UserPassword
from io import BytesIO
from service.sysMenuService import SysMenuService
from service.sysRoleService import SysRoleService
from service.sysPostService import SysPostService
from service.sysDeptService import SysDeptService
from service.sysUserRoleService import SysUserRoleService
from utils.excel_util import ExcelUtil
from excel.sysUserExcel import SysUserExcel
from passlib.hash import bcrypt
from tortoise import transactions
from utils.response import ResponseUtil

class PydanticConfig:
    alias_generator = lambda x: ''.join(word.capitalize() if i else word for i, word in enumerate(x.split('_')))
    allow_population_by_field_name = True


User_Pydantic = pydantic_model_creator(SysUser, name="User", config_class=PydanticConfig)
bcrypt = bcrypt.using(rounds=10)

menu_service = SysMenuService()
role_service = SysRoleService()
post_service = SysPostService()
dept_service = SysDeptService()
user_role_service = SysUserRoleService()


class SysUserService:

    @staticmethod
    async def get_user_by_username(username: str) -> Optional[SysUser]:
        """根据用户名获取用户信息"""
        user = await SysUser.get_or_none(user_name=username, status='0')
        if not user:
            return None
        return await User_Pydantic.from_tortoise_orm(user)

    @staticmethod
    async def create_user(user_data: UserCreate, creator: str) -> SysUser:
        """创建系统用户"""
        async with transactions.in_transaction() as transaction:
            try:
                # 检查用户名是否已存在
                if await SysUser.filter(user_name=user_data.user_name).exists():
                    raise HTTPException(status_code=400, detail="用户名已存在")

                # 获取岗位和角色ID列表
                post_ids = user_data.post_ids
                role_ids = user_data.role_ids

                # 创建用户字典，排除 post_ids 和 role_ids
                user_dict = user_data.dict(exclude={'post_ids', 'role_ids'}, exclude_unset=True)
                user_dict["create_by"] = creator
                user_dict["create_time"] = datetime.now()
                user_dict["status"] = '0'

                # 对密码进行bcrypt加密
                if user_dict.get('password'):
                    user_dict['password'] = bcrypt.hash(user_dict['password'])

                # 创建用户
                user_obj = await SysUser.create(**user_dict)

                # 创建用户-岗位关联
                if post_ids:
                    for post_id in post_ids:
                        await SysUserPost.create(
                            user_id=user_obj.user_id,
                            post_id=post_id,
                            status='0',  # 添加状态
                            create_by=creator,
                            create_time=datetime.now()
                        )

                # 创建用户-角色关联
                if role_ids:
                    for role_id in role_ids:
                        await SysUserRole.create(
                            user_id=user_obj.user_id,
                            role_id=role_id,
                            status='0',  # 添加状态
                            create_by=creator,
                            create_time=datetime.now()
                        )

                user_pydantic = await User_Pydantic.from_tortoise_orm(user_obj)

                # 转换为字典并移除密码字段
                user_dict = user_pydantic.dict()
                user_dict.pop('password', None)

                # 重新构建 Pydantic 模型
                return User_Pydantic.construct(**user_dict)

            except Exception as e:
                # 如果发生任何错误，事务会自动回滚
                raise HTTPException(status_code=400, detail=str(e))

    @staticmethod
    async def get_user_by_id(user_id: int) -> UserInfo:
        """根据ID获取用户详情，包含角色ID列表和岗位ID列表"""
        # 获取用户基本信息
        user = await SysUser.get_or_none(user_id=user_id, status='0')
        if not user:
            raise HTTPException(status_code=201, detail="用户不存在")

        role_list = await role_service.get_role_list_by_user(user_id)
        role_ids = [item['roleId'] for item in role_list]

        # # 获取用户关联的角色ID列表
        # role_ids = await SysUserRole.filter(
        #     user_id=user_id,
        #     status='0'
        # ).values_list('role_id', flat=True)

        # 获取用户关联的岗位ID列表
        post_ids = await SysUserPost.filter(
            user_id=user_id,
            status='0'
        ).values_list('post_id', flat=True)

        # 转换用户对象为 Pydantic 模型
        user_pydantic = await User_Pydantic.from_tortoise_orm(user)
        user_dict = user_pydantic.dict(exclude_none=True)

        # 角色ID列表和岗位ID列表
        user_dict['post_ids'] = list(post_ids)
        user_dict['role_ids'] = role_ids
        user_dict['roles'] = role_list

        # 使用 UserInfo schema 进行数据转换
        return UserInfo(**user_dict)

    @staticmethod
    async def update_user(user_id: int, user_data: UserUpdate, updater: str) -> SysUser:
        """更新系统用户"""
        async with transactions.in_transaction("default") as transaction:  # 指定使用 default 连接
            try:
                user = await SysUser.get_or_none(user_id=user_id, status='0')
                if not user:
                    raise HTTPException(status_code=201, detail="用户不存在")

                # 如果修改了用户名，检查是否与其他用户重复
                if user_data.user_name and user_data.user_name != user.user_name:
                    if await SysUser.filter(user_name=user_data.user_name).exists():
                        raise HTTPException(status_code=400, detail="用户名已存在")

                # 获取岗位和角色ID列表
                post_ids = user_data.post_ids
                role_ids = user_data.role_ids

                # 准备用户更新数据，排除空值
                update_dict = {
                    k: v for k, v in user_data.dict(
                        exclude={'post_ids', 'role_ids'},
                        exclude_unset=True,
                        exclude_none=True
                    ).items()
                    if v is not None
                }

                # 处理密码
                if 'password' in update_dict and update_dict['password']:
                    update_dict["password"] = bcrypt.hash(update_dict['password'])
                else:
                    update_dict.pop("password", None)

                # 添加更新人和更新时间
                update_dict["update_by"] = updater
                update_dict["update_time"] = datetime.now()

                # 更新用户基本信息
                await user.update_from_dict(update_dict).save()

                # 更新用户-岗位关联
                if post_ids is not None:
                    # 删除原有关联
                    await SysUserPost.filter(user_id=user_id).delete()
                    # 创建新关联
                    if post_ids:
                        for post_id in post_ids:
                            await SysUserPost.create(
                                user_id=user_id,
                                post_id=post_id,
                                status='0',
                                create_by=updater,
                                create_time=datetime.now()
                            )

                # 更新用户-角色关联
                if role_ids is not None:
                    # 删除原有关联
                    await SysUserRole.filter(user_id=user_id).delete()
                    # 创建新关联
                    if role_ids:
                        for role_id in role_ids:
                            await SysUserRole.create(
                                user_id=user_id,
                                role_id=role_id,
                                status='0',
                                create_by=updater,
                                create_time=datetime.now()
                            )

                # 重新获取更新后的用户信息
                updated_user = await SysUser.get(user_id=user_id)
                return await User_Pydantic.from_tortoise_orm(updated_user)

            except Exception as e:
                raise HTTPException(status_code=400, detail=str(e))

    @staticmethod
    async def delete_users(user_ids: List[int], updater: str) -> int:
        """批量逻辑删除用户"""
        update_data = {
            "status": '2',  # 设置状态为删除
            "update_by": updater,
            "update_time": datetime.now()
        }
        # 更新状态而不是物理删除
        delete_count = await SysUser.filter(user_id__in=user_ids, status='0').update(**update_data)
        return delete_count

    @staticmethod
    async def get_user_page(params: PageParams) -> Tuple[List[SysUser], int]:
        """分页获取用户列表"""
        query = SysUser.filter(status='0')

        # 构建查询条件
        if params.user_name:
            query = query.filter(user_name__icontains=params.user_name)
        if params.phonenumber:
            query = query.filter(phonenumber__icontains=params.phonenumber)
        if params.dept_id:
            query = query.filter(dept_id=params.dept_id)
        if params.status:
            query = query.filter(status=params.status)
        if params.use_flag:
            query = query.filter(use_flag=params.use_flag)

        if params.role_id and params.is_allocated:
            user_id_list = await user_role_service.find_user_id_list_by_role_id(role_id=params.role_id)
            query = query.filter(user_id__in=user_id_list)

        if params.role_id and params.is_un_allocated:
            user_id_list = await user_role_service.find_user_id_list_by_role_id(role_id=params.role_id)
            query = query.filter(user_id__not_in=user_id_list)

        # 获取总数
        total = await query.count()

        # 分页查询
        users = await query.offset((params.page_num - 1) * params.page_size) \
            .limit(params.page_size) \
            .order_by("-create_time")

        # 转换为带驼峰命名的响应数据
        pydantic_users = [await User_Pydantic.from_tortoise_orm(user) for user in users]

        return pydantic_users, total

    @staticmethod
    async def export_users_excel() -> BytesIO:
        """导出所有用户数据到Excel"""
        users = await SysUser.filter(status='0').all()
        return await SysUserExcel.export_users(users)

    @staticmethod
    def get_import_template() -> BytesIO:
        """获取用户导入模板"""
        return SysUserExcel.get_import_template()

    @staticmethod
    async def import_users(file: UploadFile, creator: str) -> Tuple[int, List[str]]:
        """导入用户数据"""
        content = await file.read()
        file_bytes = BytesIO(content)

        try:
            users_data = SysUserExcel.parse_import_users(file_bytes)
        except ValueError as e:
            raise HTTPException(status_code=400, detail=str(e))
        except Exception as e:
            raise HTTPException(status_code=400, detail="Excel文件格式错误")

        success_count = 0
        error_msgs = []

        for index, user_data in enumerate(users_data, start=1):
            try:
                if await SysUser.filter(user_name=user_data['user_name']).exists():
                    error_msgs.append(f"第{index}行: 用户名 {user_data['user_name']} 已存在")
                    continue

                user_data['create_by'] = creator
                user_data['create_time'] = datetime.now()
                user_data['status'] = '0'

                await SysUser.create(**user_data)
                success_count += 1

            except Exception as e:
                error_msgs.append(f"第{index}行: {str(e)}")

        return success_count, error_msgs

    async def getInfo(self, user_id: int) -> dict:
        user = await self.get_user_by_id(user_id)
        user_dict = user.dict(
            exclude={'password'},
            exclude_none=True,
            by_alias=True
        )

        role_key_list = await role_service.get_role_keys_by_user(user_id)

        if user_id == 1:
            perms_list = ["*:*:*"]
        else:
            perms_list = await menu_service.get_perms(user_id)

        for key, value in user_dict.items():
            if isinstance(value, datetime):
                user_dict[key] = value.isoformat()

        return {
            "user": user_dict,
            "permissions": perms_list,
            "roles": role_key_list
        }

    @staticmethod
    async def get_add_info(user_id: int):
        post_list = await post_service.get_all_list()
        role_list = await role_service.get_role_list_by_user(user_id)

        return {
            "posts": post_list,
            "roles": role_list
        }

    @staticmethod
    async def change_status(user: UserChange, updater: str):
        # 获取用户基本信息
        obj = await SysUser.get_or_none(user_id=user.user_id, status='0')
        if not obj:
            raise HTTPException(status_code=201, detail="用户不存在")

        update_dict = user.dict(exclude_unset=True)
        update_dict["update_by"] = updater
        update_dict["update_time"] = datetime.now()

        await obj.update_from_dict(update_dict).save()
        return await User_Pydantic.from_tortoise_orm(obj)

    @staticmethod
    async def get_profile(user_id: int) -> Dict:
        """根据ID获取用户详情，包含角色列表和岗位列表的完整信息"""
        # 获取用户基本信息
        user = await SysUser.get_or_none(user_id=user_id, status='0')
        if not user:
            raise HTTPException(status_code=201, detail="用户不存在")

        # 转换用户对象为 Pydantic 模型
        user_pydantic = await User_Pydantic.from_tortoise_orm(user)
        user_dict = user_pydantic.dict(exclude_none=True)

        # 获取完整的角色信息
        roles = await role_service.get_role_list_by_user(user_id)

        # 获取完整的岗位信息
        posts = await post_service.get_post_list_by_user(user_id)

        # 处理角色名称组合和ID数组
        role_group = ','.join([role.get('roleName', '') for role in roles if role.get('roleName')])
        role_ids = [role.get('roleId') for role in roles if role.get('roleId')]

        # 处理岗位名称组合和ID数组
        post_group = ','.join([post.get('postName', '') for post in posts if post.get('postName')])
        post_ids = [post.get('postId') for post in posts if post.get('postId')]

        # 直接解构 UserInfo，移除 user 嵌套
        user_info = UserInfo(**user_dict).dict(by_alias=True)

        # 部门信息 通过user里的dept_id查询
        dept = await dept_service.get_by_id(user_dict['dept_id'])

        # 构建返回字典
        return {
            **user_info,
            "roles": roles,
            "posts": posts,
            "roleIds": role_ids,
            "postIds": post_ids,
            "roleGroup": role_group,
            "postGroup": post_group,
            "dept": dept
        }

    @staticmethod
    async def update_profile_user(user_id: int, user_data: UserProfile, updater: str) -> SysUser:
        """更新系统用户"""
        try:
            user = await SysUser.get_or_none(user_id=user_id, status='0')
            if not user:
                raise HTTPException(status_code=201, detail="用户不存在")

            # 准备用户更新数据，排除空值
            update_dict = {
                k: v for k, v in user_data.dict(
                    exclude={'post_ids', 'role_ids'},
                    exclude_unset=True,
                    exclude_none=True
                ).items()
                if v is not None
            }

            if update_dict.get("password") is not None:
                update_dict["password"] = bcrypt.hash(update_dict["password"])

            # 添加更新人和更新时间
            update_dict["update_by"] = updater
            update_dict["update_time"] = datetime.now()

            # 更新用户基本信息
            await user.update_from_dict(update_dict).save()
            response = UserProfile(**update_dict)
            return response.dict(by_alias=True, exclude_none=True)

        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))

    async def update_password(self, user_id, user: UserPassword, updater: str):
        # 获取用户基本信息
        obj = await SysUser.get_or_none(user_id=user_id, status='0')
        if not obj:
            raise HTTPException(status_code=201, detail="用户不存在")

        update_dict = user.dict(exclude_unset=True)

        exist_user = await self.encrypt_password(obj.user_name, user.oldPassword)
        if exist_user is None:
            raise HTTPException(status_code=201, detail="旧密码错误")

        update_dict['password'] = bcrypt.hash(user.newPassword)
        update_dict["update_by"] = updater
        update_dict["update_time"] = datetime.now()

        await obj.update_from_dict(update_dict).save()

        # update_dict.pop('password', None)
        return await User_Pydantic.from_tortoise_orm(obj)

    @staticmethod
    async def encrypt_password(username: str, password: str) -> Optional[SysUser]:
        """验证用户密码并返回用户信息"""
        user_service = SysUserService()
        user = await user_service.get_user_by_username(username)
        if not user:
            return None

        if bcrypt.verify(password, user.password):
            return user

        return None

    async def get_auth_role_info(self, user_id: int, current_user_id: int) -> Dict:
        user_info = await self.get_user_by_id(user_id)
        role_list = await role_service.get_role_list_by_user(current_user_id)
        check_role_list = await role_service.get_role_list_by_user(user_id)
        check_role_ids = {role['roleId'] for role in check_role_list}

        for role in role_list:
            role['flag'] = role['roleId'] in check_role_ids

        return {
            "user": user_info,
            "roles": role_list
        }

    @staticmethod
    async def batch_auth_roles(user_id: int, role_ids: str, creator: str):
        role_id_list = [int(item) for item in role_ids.split(',')]
        await user_role_service.batch_create_by_roles(user_id=user_id, role_id_list=role_id_list, creator=creator)

